/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.client;

import lk.mrt.cse.wihidum.util.*;
import org.apache.axiom.om.*;
import org.apache.axiom.soap.*;
import org.apache.axis2.*;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.context.*;
import org.apache.axis2.client.*;
import org.apache.log4j.*;

/**
 * This exposes methods for publishers to send events to be pulished to a broker.
 * Publisher web services or client applications can use this class for publishing events.
 * Hence, this acts as an interface between the publishing applications and the broker service.
 */
public class PublicationClient
{
    private static final Logger logger = Logger.getLogger (PublicationClient.class);

    private PublicationClientBean clientbean;

    /**
     * Default constructor
     *
     * @param clientBean Publication client bean
     */
    public PublicationClient (PublicationClientBean clientBean)
    {
        this.clientbean = clientBean;
    }

    /**
     * Publishes an event at a broker
     *
     * @return Response to the publication
     */
    public String publish ()
      {
          try
          {
              logger.debug ("Sending publication message");

              ServiceClient serviceClient = new ServiceClient ();

              OperationClient opClient = serviceClient.createClient(ServiceClient.ANON_OUT_ONLY_OP);

              MessageContext outMsgCtx = new MessageContext();
              Options opts = outMsgCtx.getOptions();

              String brokerAddress = this.getBrokerAddress();
              if(brokerAddress != null)
              {
                  opts.setTo(clientbean.getBrokerAddress());
                  opts.setAction(WihidumConstants.Operations.PUBLISH);

                  String SOAPVersion = opts.getSoapVersionURI();
                  if (SOAPVersion==null)
                      SOAPVersion= SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;

                  SOAPEnvelope publicationEnvelope = this.createPublicationRequest (SOAPVersion);

                  outMsgCtx.setEnvelope(publicationEnvelope);

                  opClient.addMessageContext(outMsgCtx);
                  opClient.execute(true);
                  return "Publication process completed successfully";
              }
              else
                  return "Broker services are not provided by Wihidum, Yet.";
          }
          catch (AxisFault ex)
          {
              ex.printStackTrace();
              return "Error in sending publication";
          }
      }

    private String getBrokerAddress()
    {
        if(BrokerList.isEmpty())
        {
            String adminAddress=clientbean.getAdminAddress().getAddress();
            String publisherAddress=clientbean.getPublisherAddress().getAddress();
            BrokerRetrievalClient brokerRetrievalClient=new BrokerRetrievalClient();
            brokerRetrievalClient.retrieveBrokers(adminAddress,publisherAddress);         
        }
        if(BrokerList.isEmpty())
        {
            logger.info("Broker List is still empty. Therefore the broker address provide from the UI is taken.");
            return clientbean.getBrokerAddress().getAddress();
        }
        else
        {
            BrokerList.shuffleBrokerList();
            logger.info("Broker Address is taken from client's broker list.");
            return BrokerList.getTopBroker();
        }
    }


    /**
     * Creates the message for publication request
     *
     * @return Returns the OMElement which contains publication message
     */
    private SOAPEnvelope createPublicationRequest (String SOAPURI)
    {
        try
        {
        SOAPFactory soapFactory = null;
        if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
              soapFactory = OMAbstractFactory.getSOAP11Factory();
        else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
              soapFactory = OMAbstractFactory.getSOAP12Factory();
        else throw new Exception("Unknown SOAP version");

        SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();

        //declare the used namespaces in the SOAP Envelope
        OMNamespace wseventingNS = soapFactory.createOMNamespace(WSEventingConstants.EVENTING_NAMESPACE,WSEventingConstants.EVENTING_PREFIX);
        soapEnvelope.declareNamespace(wseventingNS);
        OMNamespace wsaNS = soapFactory.createOMNamespace(WSEventingConstants.ADDRESSING_NAMESPACE,WSEventingConstants.ADDRESSING_PREFIX);
        soapEnvelope.declareNamespace(wsaNS);
        OMNamespace wihidumNS = soapFactory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE,WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);
        soapEnvelope.declareNamespace(wihidumNS);

        OMFactory factory = OMAbstractFactory.getOMFactory ();

        SOAPHeader soapHeader = soapEnvelope.getHeader();

        OMElement publicationHeader = factory.createOMElement ("PublicationHeader", wihidumNS);
        
        OMElement replyTo = factory.createOMElement ("ReplyTo", wihidumNS);
        OMElement address = factory.createOMElement ("Address", wihidumNS);
        address.setText (clientbean.getPublisherAddress ().getAddress ());
        replyTo.addChild (address);
        publicationHeader.addChild (replyTo);

        OMElement topicElement = factory.createOMElement ("Topic", wihidumNS);
        topicElement.setText (clientbean.getTopic ());
        publicationHeader.addChild (topicElement);

        soapHeader.addChild(publicationHeader);
            
        SOAPBody soapBody = soapEnvelope.getBody();
        soapBody.setText(clientbean.getPublicationText ());

        return soapEnvelope;
        }
        catch (Exception ex)
        {
            logger.error (ex);
            return null;
        }
    }

}
